package com.atguigu.tingshu.common.caChe;

import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.model.album.AlbumInfo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * @author: fenghao
 * @date: 2025/9/10 18:30
 * @description:
 */
@Component
@Slf4j
@Aspect
public class TsCacheAspect {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Around("@annotation(tsCache)")
    public Object doBasicProfiling(ProceedingJoinPoint point,TsCache tsCache) throws Throwable {
        // 定义个Object对象
        Object object = new Object();
        // 本质：将getAlbumInfo(albumId)方法中的业务逻辑在这个类中实现！
        // key = 前缀 + 方法参数(albumId) + 后缀 | key = 前缀 + 方法参数(albumId)
        // 获取目标方法的参数
        Object[] args = point.getArgs();
//        String dataKey = tsCache.keyPrefix() + Arrays.asList(args) + tsCache.keySuffix();
        String dataKey = tsCache.keyPrefix() + Arrays.asList(args);
        try {
            // 从缓存中获取数据
            object = this.redisTemplate.opsForValue().get(dataKey);
            if(null == object){
                // 定义一个锁的key
                String lockKey = dataKey + ":lock";
                // 缓存中没有数据
                RLock lock = redissonClient.getLock(lockKey);
                lock.lock();
                try {
                    // 获取缓存数据
                    object = this.redisTemplate.opsForValue().get(dataKey);
                    if(null != object){
                        System.out.println("缓存中有数据");
                        return object;
                    }
                    // 业务逻辑
                    object = point.proceed();
                    // 判断数据库中是否有数据
                    if(null == object){
                        // 防止穿透，缓存一个空值
                        this.redisTemplate.opsForValue().set(dataKey,new Object(), RedisConstant.CACHE_TEMPORARY_TIMEOUT, TimeUnit.SECONDS);
                        return new Object();
                    }
                    // 将真实数据存储到缓存
                    this.redisTemplate.opsForValue().set(dataKey,object,RedisConstant.CACHE_TIMEOUT,TimeUnit.SECONDS);
                    return object;
                } catch (Throwable e) {
                    log.error(e.getMessage(),e);
                } finally {
                    // 解锁
                    lock.unlock();
                }
            }else {
                // 缓存中有数据
                return object;
            }
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        // 数据库兜底
        return point.proceed();
    }
}
